/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import xowl.interpreter.impl.OWLDatatype;
import xowl.interpreter.impl.Sequences;

/**
 *
 * @author Laurent WOUTERS
 */
public class Values {
    public static boolean isQueryVar(xowl.lang.owl2.Expression expression) {
        return (expression instanceof xowl.lang.actions.QueryVariable);
    }
    public static boolean isDynamicExpression(xowl.lang.owl2.Expression expression) {
        if (isQueryVar(expression)) return false;
        if (expression instanceof xowl.lang.actions.Invoke) return true;
        if (expression instanceof xowl.lang.actions.Execute) return true;
        if (expression instanceof xowl.lang.actions.Query) return true;
        if (expression instanceof xowl.lang.actions.CodeVariable) return true;
        if (expression instanceof xowl.lang.actions.ArrayElement) return true;
        if (expression instanceof xowl.lang.actions.NewEntity) return true;
        if (expression instanceof xowl.lang.actions.EntityForIRI) return true;
        if (expression instanceof xowl.lang.actions.NewIndividual) return true;
        if ((expression instanceof xowl.lang.owl2.LiteralExpression) && !(expression instanceof xowl.lang.owl2.Literal)) return true;
        if (expression instanceof xowl.lang.actions.ExecutableExpression) return true;
        if (expression instanceof xowl.lang.actions.ArrayExpression) return true;
        if (expression instanceof xowl.lang.interop.JavaObjectExpression) return true;
        return false;
    }
    public static boolean isStaticExpression(xowl.lang.owl2.Expression expression) {
        if (isQueryVar(expression)) return false;
        return !isDynamicExpression(expression);
    }

    public static boolean isOfType(xowl.lang.runtime.Literal literal, String type) {
        return type.equals(literal.getMemberOf().getInterpretationOf().getHasIRI().getHasValue());
    }
    
    public static boolean equals(xowl.lang.runtime.Value left, xowl.lang.runtime.Value right) {
        if (left == null)
            return (right == null);
        if (left instanceof xowl.lang.runtime.Entity) {
            return equals_Entity((xowl.lang.runtime.Entity)left, right);
        } else if (left instanceof xowl.lang.runtime.Literal) {
            return equals_Literal((xowl.lang.runtime.Literal)left, right);
        } else if (left instanceof xowl.lang.owl2.AnonymousIndividual) {
            return equals_Anonymous((xowl.lang.owl2.AnonymousIndividual)left, right);
        } else if (left instanceof xowl.lang.runtime.Array) {
            return equals_Array((xowl.lang.runtime.Array)left, right);
        } else if (left instanceof xowl.lang.runtime.Closure) {
            return equals_Closure((xowl.lang.runtime.Closure)left, right);
        } else if (left instanceof xowl.lang.runtime.JavaObject) {
            return equals_JavaObject((xowl.lang.runtime.JavaObject)left, right);
        }
        return false;
    }
    private static boolean equals_Entity(xowl.lang.runtime.Entity left, xowl.lang.runtime.Value right) {
        if (right instanceof xowl.lang.runtime.Entity) {
            xowl.lang.runtime.Entity value = (xowl.lang.runtime.Entity)right;
            return (left.getHasIRI().getHasValue().equals(value.getHasIRI().getHasValue()));
        } else if (right instanceof xowl.lang.runtime.Literal) {
            return false;
        } else if (right instanceof xowl.lang.owl2.AnonymousIndividual) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Array) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Closure) {
            return false;
        } else if (right instanceof xowl.lang.runtime.JavaObject) {
            return false;
        }
        return false;
    }
    private static boolean equals_Literal(xowl.lang.runtime.Literal left, xowl.lang.runtime.Value right) {
        if (right instanceof xowl.lang.runtime.Entity) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Literal) {
            xowl.lang.runtime.Literal value = (xowl.lang.runtime.Literal)right;
            if (left.getMemberOf() != value.getMemberOf())
                return false;
            return (left.getLexicalValue().equals(value.getLexicalValue()));
        } else if (right instanceof xowl.lang.owl2.AnonymousIndividual) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Array) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Closure) {
            return false;
        } else if (right instanceof xowl.lang.runtime.JavaObject) {
            return false;
        }
        return false;
    }
    private static boolean equals_Anonymous(xowl.lang.owl2.AnonymousIndividual left, xowl.lang.runtime.Value right) {
        if (right instanceof xowl.lang.runtime.Entity) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Literal) {
            return false;
        } else if (right instanceof xowl.lang.owl2.AnonymousIndividual) {
            return (left == right);
        } else if (right instanceof xowl.lang.runtime.Array) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Closure) {
            return false;
        } else if (right instanceof xowl.lang.runtime.JavaObject) {
            return false;
        }
        return false;
    }
    private static boolean equals_Array(xowl.lang.runtime.Array left, xowl.lang.runtime.Value right) {
        if (right instanceof xowl.lang.runtime.Entity) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Literal) {
            return false;
        } else if (right instanceof xowl.lang.owl2.AnonymousIndividual) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Array) {
            List<xowl.lang.runtime.Value> elemLeft = toList(left);
            List<xowl.lang.runtime.Value> elemRight = toList((xowl.lang.runtime.Array)right);
            if (elemLeft.size() != elemRight.size())
                return false;
            for (int i=0; i!=elemLeft.size(); i++) {
                if (!equals(elemLeft.get(i), elemRight.get(i)))
                    return false;
            }
            return true;
        } else if (right instanceof xowl.lang.runtime.Closure) {
            return false;
        } else if (right instanceof xowl.lang.runtime.JavaObject) {
            return false;
        }
        return false;
    }
    private static boolean equals_Closure(xowl.lang.runtime.Closure left, xowl.lang.runtime.Value right) {
        if (right instanceof xowl.lang.runtime.Entity) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Literal) {
            return false;
        } else if (right instanceof xowl.lang.owl2.AnonymousIndividual) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Array) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Closure) {
            return (left == right);
        } else if (right instanceof xowl.lang.runtime.JavaObject) {
            return false;
        }
        return false;
    }
    private static boolean equals_JavaObject(xowl.lang.runtime.JavaObject left, xowl.lang.runtime.Value right) {
        if (right instanceof xowl.lang.runtime.Entity) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Literal) {
            return false;
        } else if (right instanceof xowl.lang.owl2.AnonymousIndividual) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Array) {
            return false;
        } else if (right instanceof xowl.lang.runtime.Closure) {
            return false;
        } else if (right instanceof xowl.lang.runtime.JavaObject) {
            return (left.getObject().equals(((xowl.lang.runtime.JavaObject)right).getObject()));
        }
        return false;
    }
    
    public static xowl.lang.runtime.Value getValue(xowl.interpreter.Repository repository, boolean exp) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Datatype datatype = repository.interpretAsDatatype(repository.resolveEntity(OWLDatatype.xsdBoolean));
        xowl.lang.runtime.Literal literal = new xowl.lang.runtime.Literal();
        literal.setLexicalValue(Boolean.toString(exp));
        literal.setMemberOf(datatype);
        return literal;
    }
    public static xowl.lang.owl2.Literal getExpression(xowl.lang.runtime.Literal exp) {
        xowl.lang.owl2.Literal literal = new xowl.lang.owl2.Literal();
        literal.setLexicalValue(exp.getLexicalValue());
        literal.setMemberOf(exp.getMemberOf().getInterpretationOf().getHasIRI());
        return literal;
    }
    public static xowl.lang.owl2.Literal getExpression(xowl.interpreter.IRIProvider repository, boolean exp) {
        xowl.lang.owl2.IRI datatype = repository.getIRI(OWLDatatype.xsdBoolean);
        xowl.lang.owl2.Literal literal = new xowl.lang.owl2.Literal();
        literal.setLexicalValue(Boolean.toString(exp));
        literal.setMemberOf(datatype);
        return literal;
    }
    
    public static String toString(xowl.lang.runtime.Value value) {
        if (value == null) return null;
        if (value instanceof xowl.lang.runtime.Entity) {
            return ((xowl.lang.runtime.Entity)value).getHasIRI().getHasValue();
        } else if (value instanceof xowl.lang.runtime.Literal) {
            xowl.lang.runtime.Literal lit = (xowl.lang.runtime.Literal)value;
            String datatype = lit.getMemberOf().getInterpretationOf().getHasIRI().getHasValue();
            return ("\"" + lit.getLexicalValue() + "\"^^" + datatype);
        } else if (value instanceof xowl.lang.runtime.Array) {
            xowl.lang.runtime.Array array = (xowl.lang.runtime.Array)value;
            java.lang.StringBuilder builder = new java.lang.StringBuilder("[");
            boolean first = true;
            for (xowl.lang.runtime.Value elem : toList(array)) {
                if (!first)
                    builder.append(", ");
                builder.append(toString(elem));
                first = false;
            }
            builder.append("]");
            return builder.toString();
        } else if (value instanceof xowl.lang.runtime.JavaObject) {
            return ((xowl.lang.runtime.JavaObject)value).getObject().toString();
        }
        return value.toString();
    }
    
    public static boolean isTrue(xowl.lang.runtime.Value value) {
        xowl.lang.runtime.Literal lit = (xowl.lang.runtime.Literal)value;
        if (lit.getMemberOf() == null) {
            return (lit.getLexicalValue() != null && !lit.getLexicalValue().isEmpty());
        } else {
            String iri = lit.getMemberOf().getInterpretationOf().getHasIRI().getHasValue();
            if (OWLDatatype.xsdBoolean.equals(iri))
                return "true".equalsIgnoreCase(lit.getLexicalValue());
            else if (OWLDatatype.xsdInt.equals(iri) || OWLDatatype.xsdInteger.equals(iri))
                return (!"0".equals(lit.getLexicalValue()));
            return false;
        }
    }
    
    public static List<xowl.lang.runtime.Value> toList(xowl.lang.runtime.Array expression) {
        List<xowl.lang.runtime.Value> results = new ArrayList<xowl.lang.runtime.Value>();
        List<xowl.lang.runtime.Element> elements = new ArrayList<xowl.lang.runtime.Element>(expression.getAllElements());
        Collections.sort(elements, new Comparator<xowl.lang.runtime.Element>() {
            @Override public int compare(xowl.lang.runtime.Element left, xowl.lang.runtime.Element right) {
                return left.getIndex().compareTo(right.getIndex());
            }
        });
        for (xowl.lang.runtime.Element elem : elements)
            results.add(elem.getValue());
        return results;
    }
}
